#include "SequenceSet.h"
#include "File.h"
#include "ImposibleWriteFileException.h"
#include "ImposibleReadFileException.h"
#include "ImposibleOpenFileException.h"
#include "ImposibleCloseFileException.h"
#include "NotExistNameException.h"
#include <algorithm>

#define MAX_SIZE 4096


#ifndef ZERO
	#define ZERO 0
#endif

#ifndef UNO
	#define UNO 1
#endif

#ifndef ROOT_OFFSET
	#define ROOT_OFFSET 128
#endif

int maxSizeSequenceSet = MAX_SIZE;

SequenceSet::SequenceSet()
	:filename(""),
	next(ZERO),
	offset(ZERO)
{
}

SequenceSet::SequenceSet(const SequenceSet & toCopy)
{
	next = toCopy.next;
	offset = toCopy.offset;
	filename = toCopy.filename;
	keys = toCopy.keys;
}

SequenceSet::~SequenceSet()
{
	filename.clear();
	next = ZERO;
	offset = ZERO;
	keys.clear();
}

void SequenceSet::setFilename(std::string name)
{
	filename = name;
}

void SequenceSet::getFilename(std::string &name)
{
	name = filename;
}

void SequenceSet::setOffset(int cur)
{
	offset = cur;
}

int SequenceSet::getOffset()
{
	return offset;
}

void SequenceSet::setNext(int proximo)
{	
	next = proximo;
}

int SequenceSet::getNext()
{
	return next;
}

unsigned int SequenceSet::getLevel()
{
	return ZERO;
}

int SequenceSet::add(Key *key)
{
	int retorno = S_ERROR;
	bool flag = false;
	unsigned int size = ZERO;
	std::vector<Key>::iterator current = keys.begin();

	if(key != NULL)
	{
		size = this->computeSize();

// 		std::cout << "TAMAÑO DEL NODO CON LA NUEVA CLAVE: " << ((double)(size + key->getSize())) << std::endl;
// 		std::cout << "TAMAÑO MAXIMO DEL NODO: " << ((double)(maxSizeSequenceSet * 0.9)) << std::endl;

		if( ((double)(size + key->getSize())) > ((double)(maxSizeSequenceSet * 0.9)) )
			return S_OVERFLOW;
		
		while(current != keys.end() && !flag)
		{
			if((*key) < (*current))
			{
				keys.insert(current,(*key));
				flag = true;
				retorno = S_OK;
			}
			current++;
			
		}
		if(!flag)
		{
			keys.push_back((*key));
			retorno = S_OK;
		}
		
	}
	
	return retorno;
}

int SequenceSet::update(Key *key)
{
	int retorno = S_ERROR;
	bool flag = false;
	std::vector<Key>::iterator current = keys.begin();

	if(key != NULL)
	{
		while(current != keys.end() && !flag)
		{
			if((*key) == (*current))
			{
				keys.erase(current);
				keys.insert(current, (*key));
				flag = true;
			}
			current++;
		}
		if(flag)
			retorno = S_OK;
		else 
			retorno = S_NO_EXISTE;
	}
	return retorno;
}

int SequenceSet::remove(Key *key)
{
	int retorno = S_ERROR;
	bool flag = false;
	unsigned int size = ZERO;
	std::vector<Key>::iterator current = keys.begin();

	if(key != NULL)
	{
		size = this->computeSize();

		if( ((double)(size - key->getSize())) < ((double)(maxSizeSequenceSet * 0.5)) )
			return S_UNDERFLOW;

		while(current != keys.end() && !flag)
		{
			if((*key) == (*current) && key->getOffset() == (*current).getOffset())
			{
				keys.erase(current);
				flag = true;
			}
			current++;
		}
		if(flag)
			retorno = S_OK;
		else 
			retorno = S_NO_EXISTE;
	}
	return retorno;
}

int SequenceSet::get(Key *key)
{
	int retorno = S_ERROR;
	bool flag = false;
	std::vector<Key>::iterator current = keys.begin();

	if(key != NULL)
	{
		while(current != keys.end() && !flag)
		{
			if((*key) == (*current))
			{
				key->setOffset((*current).getOffset());
				flag = true;
			}
			current++;
		}
		if(flag)
			retorno = S_OK;
		else 
			retorno = S_NO_EXISTE;
	}
	return retorno;
}

int SequenceSet::get(unsigned int pos, Key *key)
{
	int retorno = S_POSICION_INVALIDA;
	std::vector<Key>::iterator current = keys.begin();

	if(key != NULL && pos < keys.size())
	{
		current += pos;
		(*key) = (*current);
		retorno = S_OK;
	}
	return retorno;
	
}

bool SequenceSet::findLastKey(Key* key, unsigned int &pos)
{
	std::vector<Key>::iterator itCursor;
	bool find = false;

	if(key != NULL && keys.size() > ZERO)
	{
		itCursor = keys.begin();

		if((*key) < (*itCursor))
		{
			pos = ZERO;
			return false;
		}
		else
		{
			pos = ZERO;
			while((itCursor != keys.end()) && (!find))
			{
				if( (*key) < (*itCursor))
					find = true;
				else
				{
					itCursor++;
					pos++;
				}
			}
			if(find)
				return true;
		}

	}
	return false;
}

bool SequenceSet::findFirstKey(Key* key, unsigned int &pos)
{
	std::vector<Key>::iterator itCursor;
	bool find = false;

	if(key != NULL && keys.size() > ZERO)
	{
		itCursor = keys.begin();

		if((*key) < (*itCursor))
		{
			pos = ZERO;
			return false;
		}
		else
		{
			pos = ZERO;
			while((itCursor != keys.end()) && (!find))
			{
				if( (*key) < (*itCursor) || (*key) == (*itCursor))
					find = true;
				else
				{
					itCursor++;
					pos++;
				}
			}
			if(find)
				return true;
		}

	}
	return false;
}

unsigned int SequenceSet::getCount()
{
	return keys.size();
}

unsigned int SequenceSet::computeSize()
{
	unsigned int retorno = sizeof(int) + sizeof(unsigned int);
	std::vector<Key>::iterator current = keys.begin();

	while(current != keys.end())
	{
		char ptr[MAX_SIZE] = {ZERO};
		unsigned int sizeKey = ZERO;

		(*current).getKey(ptr, sizeKey);
		retorno += (sizeof(unsigned int) + sizeKey + sizeof(long int));
		current++;
	}

	return retorno;
}

void SequenceSet::load()
{
	File *file = NULL;
	char record[MAX_SIZE] = {ZERO};
	char *current = record;
	unsigned int countKey = ZERO;

	try
	{
		if(filename == "")
		{	
			throw NotExistNameException ();
		}
		file = new File();

		if((file->open(filename.c_str()))==S_ERROR)
		{	
			if(file != NULL)
				delete file;
			throw ImposibleOpenFileException ();
		}
	
		file->setCurrentPos(offset);
		if((file->read(record, MAX_SIZE))==S_ERROR )
		{	
			if(file != NULL)
				delete file;
			throw ImposibleReadFileException ();
		}
		
		if(file->close() == S_ERROR)
		{
			if(file != NULL)
				delete file;
			throw ImposibleCloseFileException ();
		}
		
		
		memcpy((char*)&next, current, sizeof(int));
		current += sizeof(int);
		memcpy((char*)&countKey, current, sizeof(unsigned int));
		current += sizeof(unsigned int);
	
		for(unsigned int i = ZERO; i < countKey; i++)
		{
			char ptr[MAX_SIZE] = {ZERO};
			unsigned int sizeKey = ZERO;
			int pos = ZERO;
			Key key;
		
			memcpy((char*)&sizeKey, current, sizeof(unsigned int));
			current += sizeof(unsigned int);	
			memcpy(ptr, current, sizeKey);
			current += sizeKey;
			memcpy((char*)&pos, current, sizeof(int));
			current += sizeof(int);
	
			key.setKey(ptr, sizeKey, pos);
	
			keys.push_back(key);
		}
		delete file;
	}
	catch ( NotExistNameException & ex1)
	{
		throw NotExistNameException ();
	}
	catch ( ImposibleOpenFileException & ex2)
	{
		throw ImposibleOpenFileException ();
	}
	catch ( ImposibleCloseFileException  & ex3)
	{
		throw ImposibleCloseFileException ();
	}
	catch ( ImposibleReadFileException  & ex3)
	{
		throw ImposibleReadFileException ();
	}
	catch ( EjerciceException & ex4)
	{
		throw EjerciceException ();
	}
}

void SequenceSet::save()
{
	File *file = NULL;
	char record[MAX_SIZE] = {ZERO};
	char *current = record;
	unsigned int countKey = keys.size();
	
	try
	{
		if(filename == "")
		{	
			throw NotExistNameException ();
		}
		file = new File();

		if((file->open(filename.c_str()))==S_ERROR)
		{	
			if(file != NULL)
				delete file;
			throw ImposibleOpenFileException ();
		}
	
		file->setCurrentPos(offset);
		
		memcpy(current, &next, sizeof(int));
		current += sizeof(int);
	
		memcpy(current, &countKey, sizeof(unsigned int));
		current += sizeof(unsigned int);
	
		std::vector<Key>::iterator currentKey = keys.begin();
	
		while(currentKey != keys.end())
		{
			char ptr[MAX_SIZE] = {ZERO};
			unsigned int sizeKey = ZERO;
			int pos = ZERO;
	
			(*currentKey).getKey(ptr, sizeKey);
			pos = (*currentKey).getOffset();
			
			memcpy(current, &sizeKey, sizeof(unsigned int));
			current += sizeof(unsigned int);
			memcpy(current, ptr, sizeKey);
			current += sizeKey;
			memcpy(current, &pos, sizeof(int));
			current += sizeof(int);
	
			currentKey++;
		}

		if((file->write(record, MAX_SIZE))==S_ERROR )
		{	
			if(file != NULL)
				delete file;
			throw ImposibleWriteFileException ();
		}
		
		if(file->close() == S_ERROR)
		{
			if(file != NULL)
				delete file;
			throw ImposibleCloseFileException ();
		}	
		
		delete file;
	}
	catch ( NotExistNameException & ex1)
	{
		throw NotExistNameException ();
	}
	catch ( ImposibleOpenFileException & ex2)
	{
		throw ImposibleOpenFileException ();
	}
	catch ( ImposibleCloseFileException  & ex3)
	{
		throw ImposibleCloseFileException ();
	}
	catch ( ImposibleWriteFileException  & ex3)
	{
		throw ImposibleWriteFileException ();
	}
	catch ( EjerciceException & ex4)
	{
		throw EjerciceException ();
	}
}

void SequenceSet::split(Key* newKey, Key* promo, int &nrrPromo)
{
	int cantidad = ZERO;
	SequenceSet* newPage = NULL;
	std::vector<Key> :: iterator it;
	unsigned int contador = ZERO;
	File* file = NULL;

	try
	{
		if(newKey != NULL)
		{
			newPage = new SequenceSet();
			newPage->setFilename(filename);
	
			cantidad = ((keys.size() + 1) / 2);
	
			// modifico provisoriamente el freespace de la pagina para que entre la nueva 
			maxSizeSequenceSet = MAX_SIZE * 2;
				
			if(S_OK != this->add(newKey))
				std::cout << "SPLIT ERROR: error en la alta de la nueva clave" << std::endl;
	
			// tomo la llave a promocionar
			it = keys.begin() + cantidad;
			(*promo) = (*it);
	
			// paso las claves a la nueva pagina
			contador = cantidad;
			it = keys.begin() + contador;
			while(contador < keys.size())
			{
				Key key = (*it);
				if(S_OK != newPage->add(&key))
					std::cout << "SPLIT ERROR: error en el transpaso de las vlaves a la nueva pagina" << std::endl;
				it++;
				contador++;
			}
	
			if(S_OK != newPage->get(ZERO, promo))
				std::cout << "SPLIT ERROR: error en la obtencion del separador"  << std::endl;
		
			
			
			
			// tengo que borrar las claves en la pagina vieja
			keys.erase(keys.begin() + cantidad, keys.end());
			// Reestablezco el maximo tamaño de pagina.
			maxSizeSequenceSet = MAX_SIZE;

// 			std::cout << std::endl << "SPLIT: pagina izquierda"  << std::endl << std::endl;
// 			this->print();
	
			

			if(filename == "")
			{	
				throw NotExistNameException ();
			}
			file = new File();
	
			if((file->open(filename.c_str()))==S_ERROR)
			{	
				if(file != NULL)
					delete file;
				if(newPage != NULL)
					delete newPage;
				throw ImposibleOpenFileException ();
			}
	
			nrrPromo = file->getSize((char*)filename.c_str());

			if(file->close() == S_ERROR)
			{
				if(file != NULL)
					delete file;
				if(newPage != NULL)
					delete newPage;
				throw ImposibleCloseFileException ();
			}
			
			newPage->setOffset(nrrPromo);
			newPage->setNext(this->next);
			this->next = nrrPromo;
			delete file;
			
//			std::cout << std::endl << "SPLIT: clave a promocionar"  << std::endl;
// 			promo->print();
// 			std::cout << "SPLIT: nrr a promocionar " << nrrPromo << std::endl;
// 			
// 			std::cout << std::endl << "SPLIT: pagina derecha"  << std::endl << std::endl;
// 			newPage->print();
			newPage->save();
			this->save();
			
//			std::cout << std::endl << "SPLIT: pagina izquierda"  << std::endl << std::endl;
// 			this->print();
		
 			
			
			
			delete newPage;
			
		}
	}
	catch ( NotExistNameException & ex1)
	{
		throw NotExistNameException ();
	}
	catch ( ImposibleOpenFileException & ex2)
	{
		throw ImposibleOpenFileException ();
	}
	catch ( ImposibleCloseFileException  & ex3)
	{
		throw ImposibleCloseFileException ();
	}
	catch ( EjerciceException & ex4)
	{
		throw EjerciceException ();
	}
}

void SequenceSet::print()
{
	
	std::cout << "Filename: " << filename << std::endl;
	std::cout << "Offset: " << offset << std::endl;
	std::cout << "Proximo nodo: " << next << std::endl;
	std::cout << "Cantidad de Claves: " << keys.size() << std::endl;

	std::vector<Key>::iterator current = keys.begin();
	unsigned int i = ZERO;

	while(current != keys.end())
	{
		std::cout << i << ")  ";
		(*current).print();
		current++;
		i++;
	}
}

const SequenceSet &SequenceSet::operator=(const SequenceSet &toAssign)
{
	if(this != &toAssign)
	{
		next = toAssign.next;
		offset = toAssign.offset;
		filename = toAssign.filename;
		keys = toAssign.keys;
	}
	return *this;
}







